package com.moonhill.bt;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Vector;

import javax.bluetooth.DataElement;
import javax.bluetooth.DiscoveryAgent;
import javax.bluetooth.LocalDevice;
import javax.bluetooth.ServiceRecord;
import javax.bluetooth.UUID;
import javax.microedition.io.Connector;
import javax.microedition.io.StreamConnection;
import javax.microedition.io.StreamConnectionNotifier;

public abstract class BTServer implements DataReaderListener {
	public static final int ATTR_PROVIDER_NAME = 0x102;
	public static final String BTID = "F0E0D0C0B0A000908070605040302010";
	// public static final String SAY_HELLO = "hill,myname=";
	private LocalDevice localDevice;
	private StreamConnectionNotifier notifier;
	private BTConfig config;
	private BTGame game;
	private WaitClientThread waitThread;
	private Vector clients;
	// app msg
	private Vector msgQueue;
	private GameThread gameThread;
	private boolean discard;
	private String hostName;

	public BTServer(BTConfig config) {
		this.config = config;
		clients = new Vector();
		msgQueue = new Vector();
	}

	public void startCreateServer() {
		new SetupServerThread().start();
	}

	public void destroyServer() {
		discard = true;
		synchronized (this) {
			this.notifyAll();
		}
		if (gameThread != null) {
			try {
				gameThread.join();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		if (msgQueue != null) {
			msgQueue.removeAllElements();
			msgQueue = null;
		}
		if (clients != null) {
			for (int i = 0; i < clients.size(); i++) {
				ClientPlayer cp = (ClientPlayer) clients.elementAt(i);
				cp.beDrop();
			}
			clients = null;
		}

		if (waitThread != null) {
			try {
				waitThread.interrupt();
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			waitThread = null;
		}
		if (notifier != null) {
			try {
				notifier.close();
				notifier = null;
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		}
		this.localDevice = null;
		System.gc();

	}

	protected void broadCast(byte[] msg) {
		// first,send the msg to the server
		System.out.println("in BTServer.broadCast = " + msg.length);
		this.onReceivePlayerMsg(msg);
		// then to the clients

		for (int i = 0; i < clients.size(); i++) {
			ClientPlayer cp = (ClientPlayer) clients.elementAt(i);
			cp.beSentBTMsg(BTGame.getBTMsg(msg));
		}

	}

	public void sendMsg(byte[] msg) {
		// add msg to msg queue
		onReceiveData(msg);
	}

	public BTGame getGame() {
		return game;
	}

	public void onConnectionError(DataReader reader) {

		for (int i = 0; i < clients.size(); i++) {
			ClientPlayer cp = (ClientPlayer) clients.elementAt(i);
			if (cp.reader == reader) {
				onClientDrop(cp.name);
				return;
			}

		}
	}

	class SetupServerThread extends Thread {
		public void run() {
			clients.removeAllElements();
			localDevice = null;
			notifier = null;
			waitThread = null;
			System.gc();
			try {
				localDevice = LocalDevice.getLocalDevice();
				if (localDevice.getDiscoverable() != DiscoveryAgent.GIAC) {
					boolean b = localDevice
							.setDiscoverable(DiscoveryAgent.GIAC);
					if (!b) {
						onServerCreated(false, "can't set giac");
						return;
					}
				}
				// prepare a URL to create a notifier
				StringBuffer url = new StringBuffer("btspp://");

				// indicate this is a server
				url.append("localhost:");
				// add the UUID to identify this service
				UUID uuid = new UUID(BTID, false);
				url.append(uuid.toString());

				// request all of the client not to be authorized
				// some devices fail on authorize=true
				url
						.append(";authenticate=false;authorize=false;encrypt=false;name=MHServer");

				System.out.println(url);

				// create notifier now
				notifier = (StreamConnectionNotifier) Connector.open(url
						.toString());
				ServiceRecord service = localDevice.getRecord(notifier);
				hostName = getMyName();
				if (hostName == null) {
					hostName = localDevice.getFriendlyName();
				}
				if (hostName == null) {
					hostName = "无名机";
				}

				System.out.println(" in server hostName,hostName=" + hostName);
				service.setAttributeValue(ATTR_PROVIDER_NAME, new DataElement(
						DataElement.STRING, BTUtil
								.byteArrayToHexString(hostName
										.getBytes("UTF-8"))));

				localDevice.updateRecord(service);

			} catch (Exception e) {
				e.printStackTrace();
				onServerCreated(false, e.getMessage());
				return;

			}

			clients = new Vector();
			waitThread = new WaitClientThread();
			waitThread.start();
			onServerCreated(true, "OK");
		}
	}

	class WaitClientThread extends Thread {
		public void run() {

			int maxSize = config.getMaxPlayerCount();

			while (clients.size() + 1 < maxSize && notifier != null) {

				StreamConnection conn = null;
				try {
					conn = notifier.acceptAndOpen();
				} catch (IOException e1) {
					continue;
				}

				DataInputStream dis = null;
				try {
					dis = conn.openDataInputStream();
				} catch (IOException e1) {
					try {
						conn.close();
					} catch (IOException e) {

					}
					continue;
				}
				// shake hands
				String clientName = null;
				try {
					clientName = dis.readUTF();
				} catch (IOException e1) {
					// bad client,tick
					try {
						dis.close();
					} catch (Exception e) {
					}
					try {
						conn.close();
					} catch (IOException e) {
					}
					continue;
				}
				if (clientName != null) {
					try {
						ClientPlayer cp = new ClientPlayer();
						cp.name = clientName;
						cp.dos = conn.openDataOutputStream();
						cp.conn = conn;
						cp.dis = dis;
						clients.addElement(cp);
						onClientConnected(clientName);

					} catch (IOException e) {
						// bad client,tick
						try {
							dis.close();
						} catch (Exception e2) {
						}
						try {
							conn.close();
						} catch (IOException e3) {
						}
						continue;
					}
				} else {
					// bad client,tick
					try {
						dis.close();
					} catch (Exception e) {
					}
					try {
						conn.close();
					} catch (IOException e) {
					}
					continue;

				}

			}

			if (discard) {
				return;
			}

			BTServer.this.game = null;
			msgQueue.removeAllElements();
			gameThread = null;
			System.gc();

			BTServer.this.game = config.getGameInstance();
			BTServer.this.game.setServer(BTServer.this);
			setGameAttribute();

			gameThread = new GameThread();
			gameThread.start();

			for (int i = 0; i < clients.size(); i++) {
				ClientPlayer cp = (ClientPlayer) clients.elementAt(i);
				cp.createReader();

			}

			BTServer.this.game.init();

		}
	}

	class GameThread extends Thread {
		public void run() {
			while (!discard) {
				if (BTServer.this.msgQueue.isEmpty()) {
					synchronized (BTServer.this) {
						try {
							BTServer.this.wait();
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
				}

				if (discard) {
					return;
				}

				byte[] data = (byte[]) BTServer.this.msgQueue.firstElement();
				BTServer.this.game.handleMsg(data);
				BTServer.this.msgQueue.removeElementAt(0);

			}
		}
	}

	class ClientPlayer {
		String name;
		DataReader reader;
		DataOutputStream dos;
		StreamConnection conn;
		DataInputStream dis;

		public void beDrop() {
			if (reader != null) {
				reader.cancel();
			}
			if (dos != null) {
				try {
					dos.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}

			if (conn != null) {
				try {
					conn.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}

		public void beSentBTMsg(byte[] msg) {
			if (dos != null) {
				try {
					dos.write(msg);
					// dos.flush();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}

		public void createReader() {
			if (dis != null && reader == null) {
				this.reader = new DataReader(dis, BTServer.this);
			}
		}
	}

	public synchronized void onReceiveData(byte[] msg) {
		System.out.println("in BTServer.onReceiveData,app data.len ="
				+ msg.length);
		this.msgQueue.addElement(msg);
		this.notifyAll();
	}

	public abstract void onServerCreated(boolean isOk, String info);

	public abstract void onClientConnected(String clientName);

	public abstract void onClientDrop(String clientName);

	public abstract void setGameAttribute();

	public abstract String getMyName();

	public abstract void onReceivePlayerMsg(byte[] msg);
}
